// src/services/indicator-logger.service.ts
import * as fs from 'fs';
import * as path from 'path';

/**
 * 指标数据记录选项
 */
export interface IndicatorLoggerOptions {
  /** 日志目录路径 */
  logDir?: string;
  /** 最大记录条目数 */
  maxEntries?: number;
}

/**
 * 指标日志条目数据结构 (内部使用)
 */
export interface IndicatorLogEntry {
  /** 原始周期时间戳 */
  timestamp: number;
  /** 指标值 */
  values: Record<string, number>;
  /** 对应价格 */
  price: number;
  /** 更新时间 */
  updateTime: number;
  /** 信号状态 */
  signal?: string;
}

/**
 * JSON格式的指标日志条目
 */
export interface JsonIndicatorLogEntry {
  /** ISO格式的时间戳 */
  timestamp: string;
  /** 价格 */
  price: number;
  /** 指标值 */
  indicator: Record<string, number>;
  /** 指标类型 */
  type: string;
  /** 时间周期 */
  timeframe: string;
  /** 交易对 */
  symbol: string;
  /** 信号状态 */
  signal?: string;
}

/**
 * K线数据类型
 */
export interface KlineDataItem {
  timestamp: number;
  open: number;
  high: number;
  low: number;
  close: number;
  volume: number;
  volumeCcy?: number;
  volumeCcyQuote?: number;
  confirmed?: boolean;
}

/**
 * 指标日志记录服务
 * 用于记录和管理技术指标的历史值，使用JSON格式
 */
export class IndicatorLoggerService {
  private logFile: string = '';
  private chinaTimezoneOffset = 8 * 60 * 60 * 1000; // 中国时区偏移(毫秒)
  private recentValues: IndicatorLogEntry[] = [];
  private maxEntries: number = 10;  // 默认保留最近10个周期
  private periodMs: number = 0;     // 周期时长(毫秒)
  private lastSignal: string = 'neutral'; // 默认信号状态
  private lastUpdateTime: number = 0; // 最后更新时间
  private logDir: string = '';

  /**
   * 初始化指标日志记录器
   * @param instId 交易对
   * @param periodStr 周期字符串，如"1h"
   * @param indicatorName 指标名称，如"RSI"
   * @param options 配置选项
   */
  constructor(
    private instId: string,
    private periodStr: string,
    private indicatorName: string,
    options?: IndicatorLoggerOptions
  ) {
    // 设置最大条目数
    if (options?.maxEntries) {
      this.maxEntries = options.maxEntries;
    }

    // 设置周期时长(毫秒)
    switch (periodStr) {
      case '1m': this.periodMs = 60 * 1000; break;
      case '3m': this.periodMs = 3 * 60 * 1000; break;
      case '5m': this.periodMs = 5 * 60 * 1000; break;
      case '15m': this.periodMs = 15 * 60 * 1000; break;
      case '30m': this.periodMs = 30 * 60 * 1000; break;
      case '1H': case '1h': this.periodMs = 60 * 60 * 1000; break;
      case '4H': case '4h': this.periodMs = 4 * 60 * 60 * 1000; break;
      case '1D': case '1d': this.periodMs = 24 * 60 * 60 * 1000; break;
      default: this.periodMs = 60 * 60 * 1000; // 默认1小时
    }

    // 创建日志目录
    this.logDir = options?.logDir || path.join(process.cwd(), 'logs');
    if (!fs.existsSync(this.logDir)) {
      fs.mkdirSync(this.logDir, { recursive: true });
    }

    // 创建日志文件，格式为指标名_周期.json
    const cleanPeriod = periodStr.replace('H', 'h');
    this.logFile = path.join(this.logDir, `${indicatorName}_${cleanPeriod}_${instId.replace(/-/g, '_')}.json`);
  }

  /**
   * 初始化日志内容
   * @param initialValues 初始指标值数组
   * @param prices 价格映射对象
   */
  initializeLog(
    initialValues: { timestamp: string | number, value: number }[],
    prices: Record<number, number>
  ): void {
    // 确保只保留最新的指定条目数
    const recentValues = initialValues.slice(-this.maxEntries);

    // 将指标值转换为所需格式并记录
    this.recentValues = recentValues.map(item => {
      const timestamp = typeof item.timestamp === 'string' ? parseInt(item.timestamp) : item.timestamp;
      return {
        timestamp,  // 原始K线周期时间戳
        values: { value: item.value },
        price: prices[timestamp] || 0,
        updateTime: Date.now()  // 添加更新时间字段
      };
    });

    // 写入日志文件
    this.writeLogFile();
  }

  /**
   * 初始化指标日志
   * @param indicatorResults 指标结果数组
   * @param priceMap 价格映射
   */
  initializeIndicatorLog(indicatorResults: any[], priceMap: Record<number, number>): void {
    // 提取通用格式
    const entries: IndicatorLogEntry[] = indicatorResults
      .slice(-this.maxEntries)
      .map(result => {
        const timestamp = typeof result.timestamp === 'string'
          ? parseInt(result.timestamp)
          : result.timestamp;

        return {
          timestamp,
          values: result.values || this.extractValues(result),
          price: priceMap[timestamp] || 0,
          updateTime: Date.now()
        };
      });

    // 设置并写入日志
    this.recentValues = entries;
    this.writeLogFile();
  }

  /**
   * 初始化指标日志并设置信号
   * @param indicatorResults 指标结果数组
   * @param priceMap 价格映射
   * @param signal 信号状态 (例如 'buy', 'sell', 'neutral')
   */
  initializeIndicatorLogWithSignal(
    indicatorResults: any[],
    priceMap: Record<number, number>,
    signal: string
  ): void {
    // 提取通用格式
    const entries: IndicatorLogEntry[] = indicatorResults
      .slice(-this.maxEntries)
      .map(result => {
        const timestamp = typeof result.timestamp === 'string'
          ? parseInt(result.timestamp)
          : result.timestamp;

        return {
          timestamp,
          values: result.values || this.extractValues(result),
          price: priceMap[timestamp] || 0,
          updateTime: Date.now(),
          signal: result.signal || signal // 使用数据点自己的信号，如果没有则使用默认信号
        };
      });

    // 设置默认信号状态（用于新数据点）
    this.lastSignal = signal;

    // 设置并写入日志
    this.recentValues = entries;
    this.writeLogFile();
  }

  /**
   * 更新指标值
   * @param timestamp 原始K线周期时间戳
   * @param values 指标值对象
   * @param price 收盘价
   * @param signal 可选的信号状态
   */
  updateValue(
    timestamp: number,
    values: Record<string, number>,
    price: number,
    signal?: string
  ): void {
    // 获取当前系统时间作为更新时间
    const currentUpdateTime = Date.now();
    this.lastUpdateTime = currentUpdateTime;

    // 更新信号状态
    if (signal) {
      this.lastSignal = signal;
    }

    // 检查是否是最新的条目
    if (this.recentValues.length > 0) {
      const lastItem = this.recentValues[this.recentValues.length - 1];

      // 检查是否是新周期，根据原始周期时间戳判断
      const isNewPeriod = timestamp - lastItem.timestamp >= this.periodMs;

      if (isNewPeriod) {
        // 如果已达到最大记录数，删除最旧的记录
        if (this.recentValues.length >= this.maxEntries) {
          this.recentValues.shift();
        }

        // 添加新的周期记录
        this.recentValues.push({
          timestamp,        // 原始K线周期时间戳
          values,
          price,
          updateTime: currentUpdateTime  // 当前系统时间
        });
      } else {
        // 更新最新条目，只更新值、价格和更新时间，保持原始周期时间戳不变
        this.recentValues[this.recentValues.length - 1] = {
          timestamp: lastItem.timestamp,  // 保持原始周期时间戳不变
          values,
          price,
          updateTime: currentUpdateTime   // 使用最新系统时间
        };
      }
    } else {
      // 如果数组为空，直接添加
      this.recentValues.push({
        timestamp,
        values,
        price,
        updateTime: currentUpdateTime
      });
    }

    // 更新日志文件
    this.writeLogFile();
  }

  /**
   * 简化的更新单值指标方法
   * @param timestamp 原始K线周期时间戳
   * @param value 指标值
   * @param price 收盘价
   * @param signal 可选的信号状态
   */
  updateSingleValue(
    timestamp: number,
    value: number,
    price: number,
    signal?: string
  ): void {
    this.updateValue(timestamp, { value }, price, signal);
  }

  /**
   * 更新信号状态
   * @param signal 信号状态 (例如 'buy', 'sell', 'neutral')
   */
  updateSignal(signal: string): void {
    this.lastSignal = signal;
  }

  /**
   * 获取指标名称
   * @returns 当前指标名称
   */
  getIndicatorName(): string {
    return this.indicatorName;
  }

  /**
   * 获取周期字符串
   * @returns 当前周期字符串
   */
  getPeriodStr(): string {
    return this.periodStr;
  }

  /**
   * 获取交易对ID
   * @returns 当前交易对ID
   */
  getInstId(): string {
    return this.instId;
  }

  /**
   * 直接写入JSON格式的指标日志条目
   * @param entries 要写入的日志条目数组
   */
  writeIndicatorLog(entries: JsonIndicatorLogEntry[]): void {
    if (!entries || entries.length === 0) return;

    // 按时间戳排序
    const sortedEntries = [...entries].sort((a, b) => {
      const dateA = new Date(a.timestamp);
      const dateB = new Date(b.timestamp);
      return dateA.getTime() - dateB.getTime();
    });

    // 将条目转换为JSON行
    const content = sortedEntries.map(entry => JSON.stringify(entry)).join('\n');

    // 写入日志文件
    fs.writeFileSync(this.logFile, content);
    console.log(`写入 ${entries.length} 条记录到 ${this.logFile}`);
  }

  /**
   * 将K线数据保存为txt文件
   * @param klineData K线数据数组
   * @param instId 交易对
   * @param barStr K线周期
   * @param limit 保存的K线数量，默认为100
   * @returns 保存的文件路径
   */
  static saveKlineDataToTxt(
    klineData: KlineDataItem[],
    instId: string,
    barStr: string,
    limit: number = 100,
    logDir: string = path.join(process.cwd(), 'logs')
  ): string {
    // 确保目录存在
    if (!fs.existsSync(logDir)) {
      fs.mkdirSync(logDir, { recursive: true });
    }

    // 格式化币名和周期
    const coin = instId.split('-')[0];
    const period = barStr.toUpperCase();

    // 创建文件名
    const fileName = `Kline_${coin}_${period}.txt`;
    const filePath = path.join(logDir, fileName);

    // 取最新的limit条数据
    const recentData = [...klineData].slice(-limit);

    // 按时间升序排序
    recentData.sort((a, b) => a.timestamp - b.timestamp);

    // 格式化K线数据为文本，包含所有KlineDataItem字段
    let content = '时间戳,时间,开盘价,最高价,最低价,收盘价,成交量(张),成交量(币),成交额(计价币),K线结束\n';

    // 中国时区偏移(毫秒)
    const chinaTimezoneOffset = 8 * 60 * 60 * 1000;

    recentData.forEach(item => {
      // 转换为中国时间
      const date = new Date(item.timestamp + chinaTimezoneOffset);

      // 格式化日期为 YYYY-MM-DD HH:MM:SS
      const formattedDate = date.toISOString()
        .replace('T', ' ')
        .replace('Z', '')
        .split('.')[0];

      // 格式化K线数据行，包含所有字段
      content += [
        item.timestamp,
        formattedDate,
        item.open.toFixed(4),
        item.high.toFixed(4),
        item.low.toFixed(4),
        item.close.toFixed(4),
        item.volume.toFixed(4),
        // 处理可选字段，如果不存在则用'-'表示
        item.volumeCcy !== undefined ? item.volumeCcy.toFixed(4) : '-',
        item.volumeCcyQuote !== undefined ? item.volumeCcyQuote.toFixed(4) : '-',
        item.confirmed !== undefined ? (item.confirmed ? '1' : '0') : '-'
      ].join(',') + '\n';
    });

    // 写入文件
    fs.writeFileSync(filePath, content);

    console.log(`已将最新${limit}条完整K线数据保存到: ${filePath}`);
    return filePath;
  }

  /**
   * 从不同类型的指标结果中提取值
   * @param result 指标结果
   * @returns 标准化的值对象
   */
  private extractValues(result: any): Record<string, number> {
    // 根据不同指标类型提取关键值
    if (result.value !== undefined) {
      // RSI 格式
      return { value: result.value };
    } else if (result.DIF !== undefined || result.macd !== undefined) {
      // MACD 格式，支持新旧命名
      return {
        DIF: result.DIF !== undefined ? result.DIF : result.macd,
        DEA: result.DEA !== undefined ? result.DEA : result.signal,
        MACD: result.MACD !== undefined ? result.MACD : result.histogram
      };
    } else if (result.upper !== undefined) {
      // 布林带格式
      return {
        upper: result.upper,
        middle: result.middle,
        lower: result.lower,
        pb: result.pb || 0
      };
    } else {
      // 通用格式，移除timestamp和type字段
      const values = { ...result };
      delete values.timestamp;
      delete values.type;
      return values;
    }
  }

  /**
   * 写入日志文件
   */
  private writeLogFile(): void {
    const content = this.formatJsonLogContent();
    fs.writeFileSync(this.logFile, content);
  }

  /**
   * 格式化为JSON日志内容
   */
  private formatJsonLogContent(): string {
    // 按原始周期时间戳排序
    const sortedValues = [...this.recentValues].sort((a, b) => a.timestamp - b.timestamp);

    // 转换为JSON格式
    const jsonEntries: JsonIndicatorLogEntry[] = sortedValues.map((item, index, array) => {
      // 判断是否为最新条目
      const isLatestItem = index === array.length - 1;

      // 对于最新条目，使用实际更新时间作为时间戳
      const timestampToUse = isLatestItem ? item.updateTime : item.timestamp;

      // 格式化时间戳为ISO格式
      const formattedTime = this.formatChinaTime(timestampToUse);

      // 标准化交易对名称
      const symbol = this.instId.replace(/-/g, '_');

      // 标准化时间周期
      const timeframe = this.periodStr.toLowerCase();

      // 格式化指标值，保留两位小数
      const formattedIndicator: Record<string, number> = {};
      for (const [key, value] of Object.entries(item.values)) {
        formattedIndicator[key] = parseFloat(value.toFixed(4));
      }

      // 格式化价格，保留两位小数
      const formattedPrice = parseFloat(item.price.toFixed(4));

      return {
        timestamp: formattedTime,
        price: formattedPrice,
        indicator: formattedIndicator,
        type: this.indicatorName,
        timeframe: timeframe,
        symbol: symbol,
        signal: item.signal || this.lastSignal // 优先使用数据点自己的信号
      };
    });

    // 返回格式化的JSON字符串，每行一个条目
    return jsonEntries.map(entry => JSON.stringify(entry)).join('\n');
  }

  /**
   * 获取中国时区的格式化时间，去除毫秒和时区信息
   */
  formatChinaTime(timestamp: number): string {
    const chinaDate = new Date(timestamp + this.chinaTimezoneOffset);
    // 格式化为 YYYY-MM-DDTHH:MM:SS 格式，去掉毫秒和时区
    return chinaDate.toISOString().replace('Z', '').split('.')[0];
  }
} 